﻿namespace TomatoBreak.Infrastructure
{
    using System;
    using System.Collections.Generic;
    using System.Linq.Expressions;
    using System.Web.Mvc;

    using Web;

    public class FilterRegistryActionItem<TController> : FilterRegistryItem where TController : Controller
    {
        private readonly ReflectedActionDescriptor reflectedActionDescriptor;

        public FilterRegistryActionItem(Expression<Action<TController>> action, IEnumerable<Func<IMvcFilter>> filters) : base(filters)
        {
            if (action == null)
            {
                throw new ArgumentNullException("action");
            }

            var methodCall = action.Body as MethodCallExpression;

            if ((methodCall == null) || (methodCall.Object == null) || !typeof(ActionResult).IsAssignableFrom(methodCall.Method.ReturnType))
            {
                throw new ArgumentException(ErrorMessages.FilterRegistryActionItem_Ctor_Invalid_Expression, "action");
            }

            reflectedActionDescriptor = new ReflectedActionDescriptor(methodCall.Method, methodCall.Method.Name, new ReflectedControllerDescriptor(methodCall.Object.Type));
        }

        public override bool IsMatching(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {
            if ((controllerContext != null) && (actionDescriptor != null))
            {
                var matchingDescriptor = actionDescriptor as ReflectedActionDescriptor;

                return (matchingDescriptor != null) ?
                        reflectedActionDescriptor.MethodInfo == matchingDescriptor.MethodInfo :
                        IsSameAction(reflectedActionDescriptor, actionDescriptor);
            }

            return false;
        }

        private static bool IsSameAction(ActionDescriptor descriptor1, ActionDescriptor descriptor2)
        {
            var parameters1 = descriptor1.GetParameters();
            var parameters2 = descriptor2.GetParameters();

            var same = descriptor1.ControllerDescriptor.ControllerName.Equals(descriptor2.ControllerDescriptor.ControllerName, StringComparison.OrdinalIgnoreCase) &&
                       descriptor1.ActionName.Equals(descriptor2.ActionName, StringComparison.OrdinalIgnoreCase) &&
                       (parameters1.Length == parameters2.Length);

            if (same)
            {
                for (int i = parameters1.Length - 1; i >= 0; i--)
                {
                    if (parameters1[i].ParameterType == parameters2[i].ParameterType)
                    {
                        continue;
                    }

                    same = false;
                    break;
                }
            }

            return same;
        }
    }
}